package decomposition;

import direct_methods_for_systems_solver.GaussElimination;
import matrix_operations.Matrix;

public abstract class DecompositionLUP {
	
	private static String msg = "";

	public static Matrix[] factorizing(Matrix A) {
		
		msg = "";

		if (A.getN() != A.getM()) {
			msg = "Erro! The matrix can not be facored, because is not a square matrix.";
			System.out.println(msg);
			return null;
		}

		Matrix P;
		Matrix L;
		Matrix U;

		int n = A.getN();
		int[] permutationArray = new int[n];
		double p;
		int kAux = 0;
		double valueAux = 0;

		// initalize the permutation array
		for (int i = 0; i < n; i++) {
			permutationArray[i] = i;
		}

		for (int k = 0; k < n; k++) {

			p = 0;

			for (int i = k; i < n; i++) {

				if (Math.abs(A.getValue(i, k)) > p) {

					p = Math.abs(A.getValue(i, k));
					kAux = i;
				}
			}

			if (p == 0) {

				msg = "Error, this matrix is singular!";
				System.out.println(msg);
				return null;
			}

			exchange(permutationArray, k, kAux);

			for (int i = 0; i < n; i++) {
				valueAux = A.getValue(k, i);
				A.setValue(k, i, A.getValue(kAux, i));
				A.setValue(kAux, i, valueAux);
			}

			for (int i = k + 1; i < n; i++) {

				if (A.getValue(k, k) == 0) {
					msg = "Erro! The Matrix cannot be factored with LUP method";
					System.out.println(msg);
					return null;
				}
				A.setValue(i, k, A.getValue(i, k) / A.getValue(k, k));

				for (int j = k + 1; j < n; j++) {

					A.setValue(i, j, A.getValue(i, j) - A.getValue(i, k)
							* A.getValue(k, j));
				}
			}
		}

		L = defineMatrixL(A);
		U = defineMatrixU(A);
		P = defineMatrixP(A, permutationArray);

		return (new Matrix[] { L, U, P });
	}
	
	public static Matrix solveSytsem(Matrix[] LUP, Matrix B){
		
		msg = "";
		
		if (LUP == null){
			msg = "Erro! The Matrix cannot be factored with LUP method";
			return null;
		}
		
		
		System.out.println(LUP[2].product(B));
		Matrix y = GaussElimination.solveSystem(LUP[0], LUP[2].product(B));
		return GaussElimination.solveSystem(LUP[1], y);		
	}

	private static Matrix defineMatrixP(Matrix A, int[] permutationArray) {

		Matrix P = Matrix.getNullMatrix(A.getN(), A.getN());

		for (int i = 0; i < A.getN(); i++) {
			P.setValue(i, permutationArray[i], 1);
		}

		return P;
	}

	private static Matrix defineMatrixU(Matrix A) {

		Matrix U = Matrix.getIdentityMatrix(A.getN());

		for (int i = 0; i < A.getN(); i++) {
			for (int j = i; j < A.getN(); j++) {
				U.setValue(i, j, A.getValue(i, j));
			}
		}

		return U;
	}

	private static Matrix defineMatrixL(Matrix A) {

		Matrix L = Matrix.getIdentityMatrix(A.getN());

		for (int i = 1; i < A.getN(); i++) {
			for (int j = i - 1; j >= 0; j--) {
				L.setValue(i, j, A.getValue(i, j));
			}
		}

		return L;
	}

	private static void exchange(int[] v, int i, int j) {
		int aux;

		aux = v[i];
		v[i] = v[j];
		v[j] = aux;
	}
	
	public static String getMsg(){
		return msg;
	}

}
